﻿using datatool;
using Microsoft.Win32;
using SerialPortTool;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace datatool
{
    public class CommandItem
    {
        public string Name { get; set; }
        public byte Value { get; set; }
    }

    public enum E_RCMDHISST
    {
        e_init = 0,
        e_SendReadCntCmd,
        e_WaitCnt,
        e_SendReadCmd,
        e_WaitData,
        e_exit,
    }

    /// <summary>
    /// Interaction logic for ASProUC.xaml
    /// </summary>
    public partial class ASProUC : UserControl
    {
        private SerialPort _serialPort = new SerialPort();
        private ProtocolParser _protocolParser = new ProtocolParser();

        private string m_DataDir = @"datas/";

        private byte[] _fileData;
        private const int FRAME_SIZE = 80;  // 每帧数据部分长度
        private const int CMD_LEN = 51;  // 命令数据字段数
        private const int TASK_TIMEOUT_SEC = 5; // 任务超时时间， 5秒

        private const byte CMD_QUERY_TRACECNT = 0x00; // 
        private const byte CMD_QUERY_PHACNT = 0x01; // 
        private const byte CMD_QUERY_TRACEDAT = 0x02; // 
        private const byte CMD_QUERY_PHADAT = 0x03; // 
        private const byte CMD_PHADAT_CLEAR = 0x04; // 
        private const byte CMD_TRACEDAT_CLEAR = 0x05; // 
        private const byte CMD_WRITE_PHADAT = 0x06; // 

        private bool m_UseNetPort = true;
        private TcpClient m_TcpClient;
        private NetworkStream m_TcpStream;
        private bool m_IsNetPortOpen = false;

        private Thread m_NetReceiveThread;

        private E_RCMDHISST m_RCmdHisSt = E_RCMDHISST.e_init;
        private UInt32 m_CmdHisTotalCnt;
        private UInt32 m_CmdHisRPtr;
        private UInt32 m_CmdHisWPtr;
        private UInt16[] m_CurReadCMDHisDat;

        private static readonly object logLock = new object();

        public ASProUC()
        {
            InitializeComponent();
            Loaded += ASProUC_Loaded;
            _protocolParser.OnFrameReceived += HandleReceivedFrame;

            System.Text.Encoding.RegisterProvider( System.Text.CodePagesEncodingProvider.Instance );

            m_CurReadCMDHisDat = new UInt16[CMD_LEN];
        }

        private void StartNetRevThread()
        {
            m_NetReceiveThread = new Thread(() =>
            {
                while (m_IsNetPortOpen)
                {
                    try
                    {
                        byte[] data = new byte[256];
                        int bytes = m_TcpStream.Read(data, 0, data.Length);
                        if (bytes > 0)
                        {
                            _protocolParser.ProcessData(data, bytes);
                        }
                    }
                    catch (Exception ex)
                    {
                        AppendLog($"接收错误: {ex.Message}");
                    }
                }
            });
            m_NetReceiveThread.IsBackground = true;
            m_NetReceiveThread.Start();
        }

        // 初始化时加载命令
        private void LoadCommandsFromConfig()
        {
            var commands = new List<CommandItem>
            {
                new() { Name = "查询历史命令总数", Value = CMD_QUERY_TRACECNT },
                new() { Name = "查询相位数据总数", Value = CMD_QUERY_PHACNT },
                new() { Name = "读取历史命令", Value = CMD_QUERY_TRACEDAT },
                new() { Name = "读取相位数据", Value = CMD_QUERY_PHADAT },
                new() { Name = "擦除相位数据", Value = CMD_PHADAT_CLEAR },
                new() { Name = "擦除历史命令", Value = CMD_TRACEDAT_CLEAR }
            };

            cmbCommand.ItemsSource = commands;
            cmbCommand.DisplayMemberPath = "Name";
            cmbCommand.SelectedValuePath = "Value";

            cmbCommand.SelectedIndex = 0;
        }



        private void ASProUC_Loaded(object sender, RoutedEventArgs e)
        {
            RefreshPorts();
            cmbBaudRate.SelectedIndex = 2;
            LoadCommandsFromConfig();
        }

        private void RefreshPorts()
        {
            cmbPorts.Items.Clear();
            foreach (string port in SerialPort.GetPortNames())
            {
                cmbPorts.Items.Add(port);
            }
            if (cmbPorts.Items.Count > 0) cmbPorts.SelectedIndex = 0;
        }

        private void UpdateButtonState(bool isOpen)
        {
            btnOpenClose.Content = isOpen ? "关闭串口" : "打开串口";
            btnOpenClose.Background = isOpen ? Brushes.LightGreen : Brushes.LightCoral;
        }

        private void AppendLog(string message)
        {
            Dispatcher.Invoke(() =>
            {
                (Window.GetWindow(this) as MainWindow)?.AppendLog(message);
            });
        }

        // 打开文件
        private void BtnOpenFile_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new OpenFileDialog
            {
                Filter = "二进制文件|*.bin",
                CheckFileExists = true
            };

            if (dialog.ShowDialog() == true)
            {
                _fileData = File.ReadAllBytes(dialog.FileName);
                txtFilePath.Text = dialog.FileName;
                progressSend.Value = 0;
            }
        }

        // 分帧发送
        private async void BtnSendFile_Click(object sender, RoutedEventArgs e)
        {
            if (_fileData == null || !(_serialPort.IsOpen || m_IsNetPortOpen))
            {
                if (_fileData == null)
                    AppendLog("文件未打开");
                else
                    AppendLog("通信口未打开");
                return;
            }

            var dialog = new ConfirmDialog($"确认写入相位数据？")
            {
                Owner = (Window)Window.GetWindow(this) // 设置父窗口
            };

            if (dialog.ShowDialog() == false)
            {
                AppendLog("取消写入相位信息");
                return;
            }

            try
            {
                btnSendFile.IsEnabled = false;
                int totalFrames = (int)Math.Ceiling(_fileData.Length / (double)FRAME_SIZE);

                for (int i = 0; i < totalFrames; i++)
                {
                    int offset = i * FRAME_SIZE;
                    int length = Math.Min(FRAME_SIZE, _fileData.Length - offset);
                    byte[] chunk = new byte[length];
                    Array.Copy(_fileData, offset, chunk, 0, length);

                    byte[] frame = BuildFrame(CMD_WRITE_PHADAT, chunk);
                    await SendFrameAsync(frame); // 异步发送

                    // 更新进度
                    progressSend.Value = (i + 1) * 100 / totalFrames;
                    await Task.Delay(500); // 发送间隔
                }
            }
            catch (Exception ex)
            {
                AppendLog($"文件发送失败：{ex.Message}");
            }
            finally
            {
                AppendLog($"文件发送完成");
                btnSendFile.IsEnabled = true;
            }
        }

        // 异步发送方法
        private async Task SendFrameAsync(byte[] frame)
        {
            await Task.Run(() => {
                lock (_serialPort)
                {
                    PortWrite(frame, 0, frame.Length);
                }
            });
            AppendLog($"已发送帧 ({frame.Length}字节)"); // 可选详细日志
        }


        // 串口操作方法
        private void BtnOpenClose_Click(object sender, RoutedEventArgs e)
        {
            if (_serialPort.IsOpen)
            {
                CloseSerialPort();
            }
            else
            {
                OpenSerialPort();
            }
        }

        private void OpenSerialPort()
        {
            try
            {
                _serialPort.PortName = cmbPorts.SelectedItem.ToString();
                _serialPort.BaudRate = int.Parse(cmbBaudRate.Text);
                _serialPort.DataReceived += SerialPort_DataReceived;
                _serialPort.Open();
                UpdateButtonState(true);
                AppendLog($"串口 {_serialPort.PortName} 已打开");
            }
            catch (Exception ex)
            {
                AppendLog($"打开失败：{ex.Message}");
            }
        }

        private void CloseSerialPort()
        {
            try
            {
                _serialPort.Close();
                _serialPort.DataReceived -= SerialPort_DataReceived;
                UpdateButtonState(false);
                AppendLog($"串口 {_serialPort.PortName} 已关闭");
            }
            catch (Exception ex)
            {
                AppendLog($"关闭失败：{ex.Message}");
            }
        }

        // 协议处理部分
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] buffer = new byte[_serialPort.BytesToRead];
            _serialPort.Read(buffer, 0, buffer.Length);
            _protocolParser.ProcessData(buffer);
        }

        private void HandleReceivedFrame(byte cmd, byte[] data)
        {
            UInt32 cnt;
            byte sts;

            switch (cmd)
            {
                case CMD_QUERY_PHACNT:
                    cnt = BitConverter.ToUInt32(data, 0);
                    AppendLog($"查询到相位信息共 {cnt} 组");
                    break;
                case CMD_QUERY_TRACECNT:
                    cnt = BitConverter.ToUInt32(data, 0);
                    AppendLog($"查询到历史命令共 {cnt} 条");
                    if(m_RCmdHisSt == E_RCMDHISST.e_WaitCnt)
                    {
                        m_CmdHisTotalCnt = cnt;
                    }
                    break;
                case CMD_QUERY_PHADAT:
                    sts = data[0];
                    cnt = BitConverter.ToUInt32(data, 1);
                    AppendLog($"相位信息 {sts}:{cnt}:{BitConverter.ToString(data, 5)}");
                    break;
                case CMD_QUERY_TRACEDAT:
                    sts = data[0];
                    cnt = BitConverter.ToUInt32(data, 1);
                    AppendLog($"历史命令 {sts}:{cnt}:{BitConverter.ToString(data, 5)}");
                    if (m_RCmdHisSt == E_RCMDHISST.e_WaitData)
                    {
                        for (int i = 0; i < CMD_LEN; i++)
                        {
                            m_CurReadCMDHisDat[i] = BitConverter.ToUInt16(data, 5 + i * 2);
                        }
                        m_CmdHisWPtr++;
                    }
                    break;
                case CMD_PHADAT_CLEAR:
                    sts = data[0];
                    AppendLog($"相位信息清除，结果：{sts}");
                    break;
                case CMD_TRACEDAT_CLEAR:
                    sts = data[0];
                    AppendLog($"历史命令清除，结果：{sts}");
                    break;
                case CMD_WRITE_PHADAT:
                    sts = data[0];
                    AppendLog($"写入相位信息，结果：{sts}");
                    break;
                default:
                    AppendLog($"未知命令：0x{cmd:X2}, 数据：{BitConverter.ToString(data)}");
                    break;
            }
        }



        // 发送按钮点击
        private void BtnSend_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                byte cmd = Convert.ToByte(cmbCommand.SelectedValue);

                if ((cmd == CMD_PHADAT_CLEAR) || (cmd == CMD_TRACEDAT_CLEAR))
                {
                    var dialog = new ConfirmDialog($"确认发送擦除命令？0x{cmd:X2}");
                    dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;

                    if (dialog.ShowDialog() == false)
                    {
                        AppendLog($"取消发送命令：0x{cmd:X2}");
                        return;
                    }

                }
                byte[] data = Array.Empty<byte>();

                // 处理空数据输入
                if (!string.IsNullOrWhiteSpace(txtData.Text))
                {
                    data = Array.ConvertAll(
                        txtData.Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries),
                        s => Convert.ToByte(s, 16)
                    );
                }

                byte[] frame = BuildFrame(cmd, data);

                if (_serialPort.IsOpen || m_IsNetPortOpen)
                {
                    PortWrite(frame, 0, frame.Length);
                    AppendLog($"发送命令：0x{cmd:X2}, 数据：{(data.Length > 0 ? BitConverter.ToString(data) : "[空数据]")}");
                }
            }
            catch (Exception ex)
            {
                AppendLog($"发送失败：{ex.Message}");
            }
        }

        private byte[] BuildFrame(byte cmd, byte[] data)
        {
            List<byte> frame = new List<byte>
            {
                0xA5,                   // 帧头
                cmd,                    // 命令
                (byte)data.Length       // 数据长度（允许为0）
            };

            if (data.Length > 0)        // 仅当有数据时添加
            {
                frame.AddRange(data);
            }

            frame.Add(CalculateChecksum(frame)); // 校验和

            return frame.ToArray();
        }

        private byte CalculateChecksum(List<byte> data)
        {
            int sum = 0;
            foreach (byte b in data) sum += b;
            return (byte)(sum & 0xFF);
        }

        public void OnClosed()
        {
            if (_serialPort.IsOpen) _serialPort.Close();

            if(m_IsNetPortOpen)
            {
                m_TcpStream.Close();
                m_TcpClient.Close();
                m_IsNetPortOpen = false;
            }
        }

        private void BtnRefresh_Click(object sender, RoutedEventArgs e) => RefreshPorts();

        private void btnNetConect_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if(!m_IsNetPortOpen)
                {
                    string ipaddr = tbIPAddr.Text;
                    int port = int.Parse(tbPort.Text);
                    m_TcpClient = new TcpClient();
                    m_TcpClient.Connect(IPAddress.Parse(ipaddr), port);

                    AppendLog("已连接到TCP服务器..");

                    // 2. 获取网络流
                    m_TcpStream = m_TcpClient.GetStream();

                    m_IsNetPortOpen = true;

                    StartNetRevThread();

                    btnNetConect.Content = "关闭串口";
                    btnNetConect.Background = Brushes.LightGreen;
                }
                else
                {
                    m_IsNetPortOpen = false;
                    m_TcpStream.Close();
                    m_TcpClient.Close();

                    btnNetConect.Content = "打开串口";
                    btnNetConect.Background = Brushes.LightCoral;
                }
                
            }
            catch (Exception exp)
            {
                AppendLog($"链接TCP服务器异常..{exp.Message}");
            }
        }

        private void rbUART_Checked(object sender, RoutedEventArgs e)
        {
            m_UseNetPort = false;
        }

        private void rbNET_Checked(object sender, RoutedEventArgs e)
        {
            m_UseNetPort = true;
        }

        private void PortWrite(byte[] buffer, int offset, int count)
        {
            try
            {
                if (m_UseNetPort)
                {
                    if(m_IsNetPortOpen)
                    {
                        m_TcpStream.Write(buffer, offset, count);
                    }
                }
                else
                {
                    if(_serialPort.IsOpen)
                    {
                        _serialPort.Write(buffer, offset, count);
                    }
                }
            }
            catch (Exception exp)
            {
                AppendLog($"发送数据异常..{exp.Message}");
            }
            
        }

        private async void btnReadAllHistoryCMD_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new ConfirmDialog($"确认读取所有历史命令？");
            dialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;

            if (dialog.ShowDialog() == false)
            {
                AppendLog($"取消读取操作。");
                return;
            }

            string filename = m_DataDir + string.Format("CMDHistory-{0}.csv", DateTime.Now.ToString("yyyyMMdd-HHmmss"));

            m_RCmdHisSt = E_RCMDHISST.e_init;
            DateTime startDT = DateTime.Now;
            byte cmd;
            byte[] data;
            byte[] frame;
            bool isExit = false;
            // 获取本地ANSI编码（中文Windows通常为GBK）
            Encoding ansicoding = Encoding.GetEncoding(0)
                                    ?? Encoding.GetEncoding("GBK");

            await Task.Run(() => {
                while (!isExit)
                {
                    switch (m_RCmdHisSt)
                    {
                        case E_RCMDHISST.e_init:
                            try
                            {
                                if (!(_serialPort.IsOpen || m_IsNetPortOpen))
                                {
                                    AppendLog($"通信端口未打开，请先打开通信接口。");
                                    m_RCmdHisSt = E_RCMDHISST.e_exit;
                                }
                                else
                                {
                                    AppendLog($"开始读取历史命令,存入：" + filename);
                                    // 自动创建日志目录
                                    var logDir = System.IO.Path.GetDirectoryName(filename);
                                    _ = Directory.CreateDirectory(logDir);

                                    // 滚动检测
                                    if (File.Exists(filename))
                                    {
                                        File.Delete(filename);  // 删除已有文件
                                    }

                                    string headStr = "No.,";
                                    for (int i = 0; i < 51; i++)
                                    {
                                        headStr += (DataDefs.m_CommMsg[i] + ",");
                                    }
                                    headStr += "\r";

                                    // 写入日志
                                    File.AppendAllText(filename, headStr, ansicoding);
                                    m_RCmdHisSt = E_RCMDHISST.e_SendReadCntCmd;
                                    m_CmdHisTotalCnt = uint.MaxValue;
                                    m_CmdHisRPtr = 0;
                                    m_CmdHisWPtr = 0;
                                }
                            }
                            catch (Exception e)
                            {
                                AppendLog($"读取历史信息异常：st{m_RCmdHisSt}, msg:{e.Message}");
                                m_RCmdHisSt = E_RCMDHISST.e_exit;
                            }
                            break;
                        case E_RCMDHISST.e_SendReadCntCmd:
                            cmd = CMD_QUERY_TRACECNT;
                            data = Array.Empty<byte>();
                            frame = BuildFrame(cmd, data);

                            PortWrite(frame, 0, frame.Length);
                            AppendLog($"发送命令：0x{cmd:X2}, 数据：{(data.Length > 0 ? BitConverter.ToString(data) : "[空数据]")}");
                            m_RCmdHisSt = E_RCMDHISST.e_WaitCnt;
                            startDT = DateTime.Now;
                            break;
                        case E_RCMDHISST.e_WaitCnt:
                            if (m_CmdHisTotalCnt != uint.MaxValue)
                            {
                                Task.Delay(300);

                                if (m_CmdHisTotalCnt > 0)
                                {
                                    AppendLog($"开始读取数据，共{m_CmdHisTotalCnt}条信息。");
                                    m_RCmdHisSt = E_RCMDHISST.e_SendReadCmd;
                                }
                                else
                                {
                                    AppendLog($"无历史数据。");
                                    m_RCmdHisSt = E_RCMDHISST.e_exit;
                                }
                            }
                            else
                            {
                                if((DateTime.Now - startDT).TotalSeconds > TASK_TIMEOUT_SEC)
                                {
                                    AppendLog($"获取历史信息总数超时，退出。");
                                    m_RCmdHisSt = E_RCMDHISST.e_exit;
                                }
                            }
                            break;
                        case E_RCMDHISST.e_SendReadCmd:
                            if (m_CmdHisRPtr >= m_CmdHisTotalCnt)
                            {
                                AppendLog($"读取完成。");
                                m_RCmdHisSt = E_RCMDHISST.e_exit;
                            }
                            else
                            {
                                cmd = CMD_QUERY_TRACEDAT;
                                data = [0, 0, 0, 0,];

                                data[0] = (byte)(m_CmdHisRPtr & 0xFF);
                                data[1] = (byte)((m_CmdHisRPtr >> 8) & 0xFF);
                                data[2] = (byte)((m_CmdHisRPtr >> 16) & 0xFF);
                                data[3] = (byte)((m_CmdHisRPtr >> 24) & 0xFF);

                                frame = BuildFrame(cmd, data);

                                m_RCmdHisSt = E_RCMDHISST.e_WaitData;
                                PortWrite(frame, 0, frame.Length);
                                m_CmdHisRPtr++;
                                AppendLog($"开始读取第{m_CmdHisRPtr}条信息。命令：0x{cmd:X2}, 数据：{(data.Length > 0 ? BitConverter.ToString(data) : "[空数据]")}");
                                startDT = DateTime.Now;
                            }
                            break;
                        case E_RCMDHISST.e_WaitData:
                            if (m_CmdHisWPtr == m_CmdHisRPtr)
                            {
                                string datStr = $"{m_CmdHisRPtr},";
                                for (int i = 0; i < CMD_LEN; i++)
                                {
                                    datStr += $"{m_CurReadCMDHisDat[i]},";
                                }

                                datStr += "\r";

                                // 写入日志
                                File.AppendAllText(filename, datStr, ansicoding);
                                m_RCmdHisSt = E_RCMDHISST.e_SendReadCmd;
                            }
                            else
                            {
                                if ((DateTime.Now - startDT).TotalSeconds > TASK_TIMEOUT_SEC)
                                {
                                    AppendLog($"获取历史信息超时，退出。ptr:{m_CmdHisRPtr}");
                                    m_RCmdHisSt = E_RCMDHISST.e_exit;
                                }
                                else
                                {
                                    Task.Delay(300);
                                }
                            }
                            break;
                        case E_RCMDHISST.e_exit:
                            isExit = true;
                            break;
                        default:
                            isExit = true;
                            break;
                    }
                }

            });
        }
    }

    // 协议解析器类
    public class ProtocolParser
    {
        public delegate void FrameReceivedHandler(byte cmd, byte[] data);
        public event FrameReceivedHandler OnFrameReceived;

        private enum ParseState { Header, Cmd, Length, Data, Checksum }
        private ParseState _currentState = ParseState.Header;
        private List<byte> _buffer = new List<byte>();
        private byte _expectedLength;
        private byte _receivedChecksum;

        public void ProcessData(byte[] data, int len)
        {
            for(int i = 0; i < len; i++)
            {
                ProcessByte(data[i]);
            }
        }

        public void ProcessData(byte[] data)
        {
            foreach (byte b in data)
            {
                ProcessByte(b);
            }
        }

        private void ProcessByte(byte b)
        {
            switch (_currentState)
            {
                case ParseState.Header:
                    if (b == 0xA5)
                    {
                        _buffer.Clear();
                        _buffer.Add(b);
                        _currentState = ParseState.Cmd;
                    }
                    break;

                case ParseState.Cmd:
                    _buffer.Add(b);
                    _currentState = ParseState.Length;
                    break;

                case ParseState.Length:
                    _buffer.Add(b);
                    _expectedLength = b;
                    _currentState = (_expectedLength > 0) ? ParseState.Data : ParseState.Checksum;
                    break;

                case ParseState.Data:
                    _buffer.Add(b);
                    if (_buffer.Count - 3 == _expectedLength) // 3 = Header + Cmd + Length
                    {
                        _currentState = ParseState.Checksum;
                    }
                    break;

                case ParseState.Checksum:
                    _receivedChecksum = b;
                    ValidateFrame();
                    _currentState = ParseState.Header;
                    break;
            }
        }

        private void ValidateFrame()
        {
            byte calculatedChecksum = CalculateChecksum(_buffer);
            if (calculatedChecksum == _receivedChecksum)
            {
                byte cmd = _buffer[1];
                byte[] payload = _buffer.Skip(3).Take(_expectedLength).ToArray();
                OnFrameReceived?.Invoke(cmd, payload);
            }
            _buffer.Clear();
        }

        private byte CalculateChecksum(List<byte> data)
        {
            int sum = 0;
            foreach (byte b in data) sum += b;
            return (byte)(sum & 0xFF);
        }
    }
}
